package process;

import inferenceEngine.InferenceEngine;
import inferenceEngine.labels.Label;
import inferenceEngine.labels.LabelManager;
import inferenceEngine.types.ConflictSolver;
import inferenceEngine.types.Inference;
import inferenceEngine.types.Meaning;

import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TreeSet;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import common.Laptop;
import common.Pair;
import common.SqliteManager;

/**
 * Support operation for GUI, hide all the complexity
 * @author Aqeel
 *
 */
public class ProcessManager {
	private InferenceEngine engine = null;
	private LinkedHashMap<String,Laptop> laptops = new LinkedHashMap<String,Laptop>();
	
	@SuppressWarnings("unchecked")
	public ProcessManager() throws Exception{
		engine = new InferenceEngine();		
	}
	
	/**
	 * Input initial labels which is collected from user, and the minimum and maximum price, then get the laptops directly
	 * @param initialLabels 				Input initial labels which is collected from user
	 * @param minimumPrice			Minimum price which user specified
	 * @param maximumPrice			Maximum price which user specified
	 * @return
	 */
	public Collection<Laptop> process ( ArrayList<Label> initialLabels, int minimumPrice, int maximumPrice ){
		engine.infer(initialLabels, minimumPrice, maximumPrice );
		getLaptopSet();
		getRecommendedLaptopSet();
		ArrayList<Laptop> results =  new ArrayList<Laptop>(  laptops.values() );
		Collections.sort( results , new Comparator<Laptop>() {
			@Override
			public int compare(Laptop o1, Laptop o2) {
				return o1.getRecommendMark() > o2.getRecommendMark()? -1:1;
			}
		} );
		return results;
	}
	
	private void getLaptopSet(){
		String sql = engine.getBasicSql();
		sql = "select * from laptopView where " + sql;
		try {
			System.out.println(sql);
			ResultSet set = SqliteManager.getResult(sql);
			ArrayList<Label> labels = engine.getRequiredLabels();
			// there are some results
			addToLaptopSet(set,labels);
		} catch (Exception e) {}
	}
	
	/**
	 * Get feedback from user, if user like the laptop, the recommended degree of corresponding labels will increase, and other labels will decrease.
	 * @param laptop
	 */
	public void likeLaptop( Laptop laptop ){
		ArrayList<Label> preferLabels = laptop.getRecommendedLabels(); 
		ArrayList<Label> recommendeLabel = engine.getRecommendedLabels();
		ArrayList<Pair<Label,Boolean>> pairs = new ArrayList<Pair<Label,Boolean>>();
		for( Label label : recommendeLabel ){
			Pair<Label,Boolean> pair = null;
			if( preferLabels.contains(label) ){
				pair = new Pair<Label,Boolean>( label, true );			
			}else{
				pair = new Pair<Label,Boolean>( label, false );
			}
			pairs.add(pair);
		}
		new LabelManager().adjustChange(pairs);
	}
	
	public void tooHeavy( Laptop laptop ){
		laptop.tooHeavy();
		engine.save();
	}
	
	public ArrayList<Label> getRequiredLabels(){
		return engine.getRequiredLabels();
	} 

	public ArrayList<Label> getRecommendedLabels(){
		return engine.getRecommendedLabels();
	} 
	
	public ArrayList<String> getFiredRules(){
		
		StringBuilder sb = new StringBuilder();
		ArrayList<String> firedRules = new ArrayList<String>();
		firedRules.add("initial labels:");
		for( Label label: engine.getStartingLabels() ){
			sb.append( "'"+label.getValue()+"'" + " ");
		}
		
		firedRules.add( sb.toString() );
		firedRules.add("\n");
		
		firedRules.add("Fired rules:");
		for( Inference infer : engine.getFiredInferences() ){
			firedRules.add( infer.toString() );
		}
		firedRules.add("\n");
		for( String string : engine.getFiredConflictSolvers() ){
			firedRules.add( string );
		}
		firedRules.add("\n");
		for( Meaning meaning : engine.getFiredMeanings() ){
			firedRules.add( meaning.toString() );
		}
		return firedRules;
	}

	private void getRecommendedLaptopSet(){
		ArrayList<Pair<String, Label>> pairs = engine.getRecommendedSql();
		for( Pair<String, Label> pair : pairs ){
			String sqlParams = pair.first;
			Label label = pair.second;
			String sql = "select * from laptopView where " + sqlParams;
			try {
				ResultSet set = SqliteManager.getResult(sql);
				// there are some results
				addToLaptopSet(set,label);
			} catch (Exception e) {}
		}
	}
	private void addToLaptopSet(ResultSet rs, Label label) {
		ResultSetMetaData metaData;
		try {
			metaData = rs.getMetaData();
			int columns=metaData.getColumnCount();
	        while(rs.next()){
	        	Laptop laptop = new Laptop( engine.getLinguisticVariables() );
	            for(int i=1;i<=columns;++i) {
	                String column = metaData.getColumnName(i);
	                String result = rs.getString(i);
	                laptop.setAttribute(column, result);
	            }
	            if( laptops.containsKey( laptop.getName() ) ){
	            	laptops.get( laptop.getName() ).addLabel(label);
	            }else{
	            	laptops.put( laptop.getName() , laptop );
	            }
	        }
		} catch (SQLException e) {
			e.printStackTrace();
		}        
	}
	/**
	 * interpret the data of result set to a set of laptops
	 */
	private void addToLaptopSet( ResultSet rs, ArrayList<Label> labels ){
		ResultSetMetaData metaData;
		try {
			metaData = rs.getMetaData();
			int columns=metaData.getColumnCount();
	        while(rs.next()){
	        	Laptop laptop = new Laptop( engine.getLinguisticVariables() );
	            for(int i=1;i<=columns;++i) {
	                String column = metaData.getColumnName(i);
	                String result = rs.getString(i);
	                laptop.setAttribute(column, result);	                
	            }
	            laptop.addLabels( labels );
	            laptops.put( laptop.getName(), laptop);
	        }
		} catch (SQLException e) {
			e.printStackTrace();
		}        
	}

	public void tooThick(Laptop laptop) {
		laptop.tooThick();
		engine.save();
	}
	
   
}
